﻿using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Runtime.Serialization;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;

namespace EcoSystem
{

    public class DataPermissionManager : ApplicationService, IDataPermissionManager, IApplicationService
    {
        private readonly IRepository<DataPermissionGrant, Guid> _repository;

        public DataPermissionManager(IRepository<DataPermissionGrant, Guid> repository)
        {
            _repository = repository;
        }

        public async Task DeleteAsync(string providerName, string providerKey, string enCode)
        {
            await _repository.DeleteAsync(x => x.ProviderName == providerName && x.ProviderKey == providerKey && x.EnCode == enCode);
        }

        public async Task<List<DataPermissionGrantDto>> GetPermissionsAsync(string providerName, string providerKey, string enCode)
        {
            var query = await _repository.GetQueryableAsync();
            query = query.AsNoTracking().Where(x => x.ProviderName == providerName && x.ProviderKey == providerKey && x.EnCode == enCode);
            var items = await query.ToListAsync();
            return ObjectMapper.Map<List<DataPermissionGrant>, List<DataPermissionGrantDto>>(items);
        }

        public async Task<bool> IsGrantedAsync(string enCode, string dataId)
        {
            if (this.CurrentUser.IsAuthenticated)
            {
                var query = await _repository.GetQueryableAsync();
                query = query.AsNoTracking().Where(x => x.DataId == dataId && x.EnCode == enCode && x.AllowAuthorize);
                var userId = this.CurrentUser.Id.ToString();
                var roles = CurrentUser.Roles ?? new string[] { };
                query = query.Where(x =>
                (x.ProviderName == "U" && x.ProviderKey == userId) ||
                (x.ProviderName == "R" && roles.Contains(x.ProviderKey)));
                return await query.AnyAsync();
            }
            else
            {
                return false;
            }
        }

        public async Task<List<DataPermissionGrantDto>> SavePermissionsAsync(SaveDataPermissionGrantsInputDto input)
        {
            await _repository.DeleteAsync(x => x.EnCode == input.EnCode &&
            x.ProviderName == input.ProviderName &&
            x.ProviderKey == input.ProviderKey &&
            x.AllowAuthorize == input.AllowAuthorize);

            var entityItems = new List<DataPermissionGrant>();
            input.DataIds.ForEach(item =>
            {
                var entity = new DataPermissionGrant()
                {
                    EnCode = input.EnCode,
                    ProviderKey = input.ProviderKey,
                    ProviderName = input.ProviderName,
                    AllowAuthorize = input.AllowAuthorize,
                    DataId = item,
                };
                EntityHelper.TrySetTenantId(entity);
                EntityHelper.TrySetId(entity, () => GuidGenerator.Create());
            });
            await _repository.InsertManyAsync(entityItems);
            return ObjectMapper.Map<List<DataPermissionGrant>, List<DataPermissionGrantDto>>(entityItems);
        }

        public async Task SetAsync(SetDataPermissionInputDto input)
        {
            var entity = await _repository.FirstOrDefaultAsync(x =>
                x.DataId == input.DataId && x.ProviderKey == input.ProviderKey && x.ProviderName == input.ProviderName && x.EnCode == input.EnCode);
            if (entity != null)
            {
                entity.AllowAuthorize = input.IsGranted;
                await _repository.UpdateAsync(entity);
            }
            else
            {
                entity = new DataPermissionGrant()
                {
                    AllowAuthorize = input.IsGranted,
                    DataId = input.DataId,
                    EnCode = input.EnCode,
                    ProviderKey = input.ProviderKey,
                    ProviderName = input.ProviderName
                };
                EntityHelper.TrySetTenantId(entity);
                EntityHelper.TrySetId(entity, () => GuidGenerator.Create());
                await _repository.InsertAsync(entity);
            }
        }
    }

}
